//------------------------------------------------------------------------------
// <copyright file="Updater.cs" company="Damian Antonowicz">
//       copyright © 2010 Damian Antonowicz
// </copyright>
// <author>Damian Antonowicz</author>
// <email>poczta@damianantonowicz.pl</email>
//------------------------------------------------------------------------------

using System;
using System.Diagnostics;
using System.IO;
using System.Security.Cryptography;
using Ionic.Zip;
using DotBeer.UI.WinFormsClient.Updater.Properties;
using System.Windows.Forms;

namespace Updater
{
    public class Updater
    {
        #region private members

        Process[] _processes;
        string workingDirPath;
        bool deleteOutdatedBuilds;
        string dotBeerPath;
        string dotbeerDir;

        #endregion

        #region constructors

        public Updater(string workingDirPath, bool deleteOutdatedBuilds)
        {
            this.workingDirPath = workingDirPath;
            this.deleteOutdatedBuilds = deleteOutdatedBuilds;
        }

        #endregion

        #region properties

        public event Action ProcessInMemory;
        public event Action ProcessExited;
        public event Action UpdateCompleted;
        public event FileHandler FileExtracted;
        public event FileHandler FileIsUpToDate;

        #endregion

        #region public methods

        public void RunDotBeer()
        {
            if (File.Exists(this.dotBeerPath))
                Process.Start(this.dotBeerPath);
        }

        public void Update()
        {
            WaitForDotBeerExit();
        }

        #endregion

        #region private methods

        private void WaitForDotBeerExit()
        {
            _processes = Process.GetProcessesByName("DotBeer.exe");

            if (_processes.Length > 0)
            {
                OnProcessInMemory();

                foreach (Process p in _processes)
                {
                    p.EnableRaisingEvents = true;
                    p.Exited += new EventHandler(p_Exited);
                }
            }
            else
            {
                CopyFiles();
            }
        }

        void p_Exited(object sender, EventArgs e)
        {
            if (_processes.Length == 0)
            {
                OnProcessExited();
                CopyFiles();
            }
        }

        private void CopyFiles()
        {
            dotbeerDir = "";

            if (this.workingDirPath == "")
            {
                string currDir = AppDomain.CurrentDomain.BaseDirectory;
                dotbeerDir = new DirectoryInfo(currDir).Parent.FullName;
            }
            else
                dotbeerDir = this.workingDirPath;

            this.dotBeerPath = Path.Combine(dotbeerDir, "DotBeer.exe");

            //string currDir = AppDomain.CurrentDomain.BaseDirectory;
            //string dotbeerDir = new DirectoryInfo(currDir).Parent.FullName;
            //string dotbeerDir = this.workingDirPath;


            string dirForOldFiles = String.Format("DotBeer v0.9.3.0");
            CreateDir(Path.Combine(dotbeerDir, dirForOldFiles));

            MoveHeroesFiles(dotbeerDir);

            if (this.deleteOutdatedBuilds)
                this.DeleteOutdatedBuilds();

            using (ZipFile zipFile = ZipFile.Read(Resources.Update))
            {
                foreach (ZipEntry zipEntry in zipFile)
                {
                    string oldFilePath = Path.Combine(dotbeerDir, zipEntry.FileName);

                    if (File.Exists(oldFilePath))
                    {
                        byte[] oldFileBytes = File.ReadAllBytes(oldFilePath);

                        Stream stream = zipEntry.OpenReader();
                        BinaryReader b = new BinaryReader(stream);
                        byte[] newFileBytes = b.ReadBytes((int)stream.Length);
                        b.Close();
                        stream.Close();

                        if (!IsHashTheSame(GetHash(oldFileBytes), GetHash(newFileBytes)))
                        {
                            MoveFile(oldFilePath, String.Format(@"{0}/{1}/{2}", dotbeerDir, dirForOldFiles, Path.GetFileName(oldFilePath)));
                            zipEntry.Extract(dotbeerDir);
                            OnFileExtracted(zipEntry.FileName, Path.Combine(dotbeerDir, zipEntry.FileName));
                        }
                        else
                        {
                            OnFileIsUpToDate(zipEntry.FileName, oldFilePath);
                        }
                    }
                    else
                    {
                        zipEntry.Extract(dotbeerDir);
                        OnFileExtracted(zipEntry.FileName, Path.Combine(dotbeerDir, zipEntry.FileName));
                    }
                }
            }

            //MoveHeroesFiles(dotbeerDir);
            OnUpdateCompleted();
        }

        private void DeleteOutdatedBuilds()
        {
            string heroesDir = Path.Combine(this.dotbeerDir, "Heroes");
            string[] fileNames = Resources.DotBeer_builds_to_delete.Split(new string[] { Environment.NewLine }, StringSplitOptions.None);

            foreach (string s in fileNames)
            {
                string filePath = Path.Combine(heroesDir, s);

                if (File.Exists(filePath))
                    File.Delete(filePath);
            }
        }

        private void MoveHeroesFiles(string dotbeerDir)
        {
            string heroesDir = Path.Combine(dotbeerDir, "Heroes");
            string sentinelDir = Path.Combine(dotbeerDir, @"Heroes\Sentinel");
            string scourgeDir = Path.Combine(dotbeerDir, @"Heroes\Scourge");

            if (Directory.Exists(sentinelDir))
                this.MoveFiles(sentinelDir, heroesDir);

            if (Directory.Exists(scourgeDir))
                this.MoveFiles(scourgeDir, heroesDir);
        }

        private void MoveFiles(string sourceDir, string destDir)
        {
            DirectoryInfo dirInfo = new DirectoryInfo(sourceDir);

            foreach (FileInfo fileInfo in dirInfo.GetFiles("*.xml"))
            {
                try
                {
                    File.Move(fileInfo.FullName, Path.Combine(destDir, fileInfo.Name));
                }
                catch
                {
                }
            }
        }

        private string GetFileVersion(string version)
        {
            return version.Substring(0, version.LastIndexOf("."));
        }

        private bool IsHashTheSame(byte[] oldFileBytes, byte[] NewFileBytes)
        {
            return Convert.ToBase64String(oldFileBytes) == Convert.ToBase64String(NewFileBytes);
        }

        private byte[] GetHash(byte[] bytes)
        {
            SHA256Managed sha = new SHA256Managed();
            return sha.ComputeHash(bytes);
        }

        private void CreateDir(string dirPath)
        {
            if (!Directory.Exists(dirPath))
                Directory.CreateDirectory(dirPath);
        }

        private void MoveFile(string oldFilePath, string newFilePath)
        {
            if (File.Exists(newFilePath))
                File.Delete(newFilePath);

            File.Move(oldFilePath, newFilePath);
        }

        private void OnProcessInMemory()
        {
            if (ProcessInMemory != null)
                ProcessInMemory();
        }

        private void OnProcessExited()
        {
            if (ProcessExited != null)
                ProcessExited();
        }

        private void OnFileExtracted(string fileName, string filePath)
        {
            if (FileExtracted != null)
                FileExtracted(this, new FileEventArgs(fileName, filePath));
        }

        private void OnFileIsUpToDate(string filename, string filePath)
        {
            if (FileIsUpToDate != null)
                FileIsUpToDate(this, new FileEventArgs(filename, filePath));
        }

        private void OnUpdateCompleted()
        {
            if (UpdateCompleted != null)
                UpdateCompleted();
        }

        #endregion
    }
}
